package com.kaisu.blog.infrastructure.interceptor;

import jakarta.annotation.Nullable;
import org.hibernate.Interceptor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.Type;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author kaisu
 */
@Component
public class JpaFilterNullInterceptor implements Interceptor {
    @Override
    public int[] findDirty(Object entity,
                           Object id,
                           Object[] currentState,
                           Object[] previousState,
                           String[] propertyNames,
                           Type[] types) {
        Set<String> nullFields = findNullableFields(entity);
        int[] results = null;
        int count = 0;
        int span = propertyNames.length;
        for (int i = 0; i < span; i++) {
            if (Objects.deepEquals(currentState[i], previousState[i])) {
                continue;
            }
            if (Objects.nonNull(currentState[i]) || nullFields.contains(propertyNames[i])) {
                results = addToResultsArray(results, count++, i, span);
            }
        }
        return (count == 0) ? new int[0] : ArrayHelper.trim(results, count);
    }

    private Set<String> findNullableFields(Object entity) {
        return Arrays.stream(entity.getClass().getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(Nullable.class))
                .map(Field::getName)
                .collect(Collectors.toSet());
    }

    private int[] addToResultsArray(int[] results, int count, int value, int span) {
        if (results == null) {
            results = new int[span];
        }
        results[count] = value;
        return results;
    }
}
